package top.v5it.japi.plus.sdk;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.StopWatch;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.SecureUtil;
import cn.hutool.crypto.asymmetric.*;
import cn.hutool.http.HttpStatus;
import cn.hutool.http.HttpUtil;
import cn.hutool.http.Method;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.fasterxml.jackson.databind.node.TextNode;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;

import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.nio.charset.Charset;
import java.security.KeyFactory;
import java.security.NoSuchAlgorithmException;
import java.security.PublicKey;
import java.security.cert.X509Certificate;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.time.Instant;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.function.BiConsumer;
import java.util.function.BiFunction;
import java.util.stream.Stream;

/**
 * 创建客户端
 *
 * @author zhanpu
 * @date 2021/3/9
 */
@Slf4j
public class JapiHttpClient {

    /**
     * 资源类型
     */
    static final String RESOURCE_TYPE = "resource_type";
    /**
     * 资源字段
     */
    private static final String RESOURCE = "resource";
    /**
     * 编码集
     */
    private static final String CHARACTER_ENCODING = "UTF-8";

    /**
     * 日志分隔线
     */
    private static final String LOG_DIVIDING_LINE = "\n++++++++++++++++++++++++++++++++++++++++++++++++++\n";
    private static final ObjectMapper MAPPER;

    static {
        MAPPER = new ObjectMapper()
                //序列化时，过虑为null的属性值不参与序列化
                .setSerializationInclusion(JsonInclude.Include.NON_NULL)
                //取消时间的转化格式,默认是时间戳,可以取消,同时需要设置要表现的时间格式
                .configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false)
                //设置日期格式化
//                .setDateFormat(new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS"))
                //反序列化时未知属性不参与
                .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
                //加入JavaTimeModule
                .registerModule(new JavaTimeModule());
    }

    /**
     * 配置信息
     */
    private final JapiClientProperties japiClientProperties;
    private final Secure secure;

    public JapiHttpClient(JapiClientProperties japiClientProperties) {
        this.japiClientProperties = japiClientProperties;
        this.secure = new Secure(japiClientProperties);
    }

    /**
     * post请求，适用无返回结果请求
     *
     * @param path 请求url的path部分，如：/api/v1/pay/transactions
     * @param body 请求参数体
     */
    public void post(String path, Object body) {
        request(Method.POST, path, writeValueAsString(body));
    }

    /**
     * post请求
     *
     * @param path  请求url的path部分，如：/api/v1/pay/transactions
     * @param body  请求参数体
     * @param clazz 响应结果类型
     * @param <T>   类型
     * @return {@link Optional<T>}
     */
    public <T> T post(String path, Object body, Class<T> clazz) {
        return readValue(request(Method.POST, path, writeValueAsString(body)), clazz);
    }

    /**
     * post请求
     *
     * @param path          请求url的path部分，如：/api/v1/pay/transactions
     * @param body          请求参数体
     * @param typeReference 响应结果类型
     * @param <T>           类型
     * @return {@link Optional<T>}
     */
    public <T> T post(String path, Object body, TypeReference<T> typeReference) {
        return readValue(request(Method.POST, path, writeValueAsString(body)), typeReference);
    }

    /**
     * put请求，适用无返回结果请求
     *
     * @param path 请求url的path部分，如：/api/v1/pay/transactions
     * @param body 请求参数体
     */
    public void put(String path, Object body) {
        request(Method.PUT, path, writeValueAsString(body));
    }

    /**
     * put请求
     *
     * @param path  请求url的path部分，如：/api/v1/pay/transactions
     * @param body  请求参数体
     * @param clazz 响应结果类型
     * @param <T>   类型
     * @return {@link Optional<T>}
     */
    public <T> T put(String path, Object body, Class<T> clazz) {
        return readValue(request(Method.PUT, path, writeValueAsString(body)), clazz);
    }

    /**
     * put请求
     *
     * @param path          请求url的path部分，如：/api/v1/pay/transactions
     * @param body          请求参数体
     * @param typeReference 响应结果类型
     * @param <T>           类型
     * @return {@link Optional<T>}
     */
    public <T> T put(String path, Object body, TypeReference<T> typeReference) {
        return readValue(request(Method.PUT, path, writeValueAsString(body)), typeReference);
    }

    /**
     * get请求，适用无返回结果请求
     *
     * @param path   请求url的path部分，如：/api/v1/pay/transactions
     * @param params 请求参数体，{@link Map<String, Object>}
     */
    public void get(String path, Map<String, Object> params) {
        request(Method.GET, getPath(path, params));
    }

    /**
     * get请求
     *
     * @param path   请求url的path部分，如：/api/v1/pay/transactions
     * @param params 请求参数体，{@link Map<String, Object>}
     * @param clazz  响应结果类型
     * @param <T>    类型
     * @return {@link Optional<T>}
     */
    public <T> T get(String path, Map<String, Object> params, Class<T> clazz) {
        return readValue(request(Method.GET, getPath(path, params)), clazz);
    }

    /**
     * get请求
     *
     * @param path          请求url的path部分，如：/api/v1/pay/transactions
     * @param params        请求参数体，{@link Map<String, Object>}
     * @param typeReference 响应结果类型
     * @param <T>           类型
     * @return {@link Optional<T>}
     */
    public <T> T get(String path, Map<String, Object> params, TypeReference<T> typeReference) {
        return readValue(request(Method.GET, getPath(path, params)), typeReference);
    }

    /**
     * post请求
     *
     * @param path   请求url的path部分，如：/api/v1/pay/transactions
     * @param params 请求参数体，{@link Map<String, Object>}
     */
    private String getPath(String path, Map<String, Object> params) {

        if (Objects.isNull(params)) return path;

        // 过虑空值
        final Map<String, Object> paramMap = MapUtil.removeNullValue(params);

        if (!params.isEmpty() && params.containsKey(RESOURCE)) {

            String resource = MapUtil.getStr(paramMap, RESOURCE);
            Optional<String> resourceApply = secure.resourceApply(MapUtil.getStr(paramMap, RESOURCE_TYPE), resource, secure.getPublicKey(CertType.SERVER), secure::encrypt);
            resourceApply.ifPresent(s -> paramMap.replace(RESOURCE, resource, s));
        }

        return HttpUtil.urlWithForm(path, paramMap, Charset.forName(CHARACTER_ENCODING), false);
    }

    /**
     * 发送请求
     *
     * @param method 请求方法
     * @param path   请求资源路径
     * @param body   请求体
     * @return 响应体
     */
    public String request(Method method, String path, String... body) {
        return request(method, path, japiClientProperties.getTimeout(), body);
    }

    /**
     * 发送请求
     *
     * @param method  请求方法
     * @param path    请求资源路径
     * @param body    请求体
     * @param timeout 超时时间
     * @return 响应体
     */
    public String request(Method method, String path, int timeout, String... body) {
        // 1970至至今的秒数
        String epochSecond = Objects.toString(Instant.now().getEpochSecond());
        // 随机串
        String nonceStr = IdUtil.fastSimpleUUID();

        // 签名消息
        String signatureMessage = secure.buildSignatureMessage(method.name(), path, epochSecond, nonceStr, Stream.of(body).allMatch(StrUtil::isBlank) ? "" : body[0]);
        log.debug("{}请求签名串\n{}{}", LOG_DIVIDING_LINE, signatureMessage, LOG_DIVIDING_LINE);

        // 签名
        String signature = secure.signature(signatureMessage);
        //封装请求头
        Map<String, String> headerMap = header(epochSecond, nonceStr, signature);
        if (log.isDebugEnabled()) {
            log.debug("{}请求头\n{}{}", LOG_DIVIDING_LINE, headerMap, LOG_DIVIDING_LINE);
        }

        // 统计用时
        StopWatch stopWatch = new StopWatch("请求ETC拓展服务平台", false);
        stopWatch.start();

        //获取配置信息
        //拼装地址
        String url = japiClientProperties.getUrl() + path;
        stopWatch.stop();

        // 发送请求
//        JapiReturnVo japiReturnVo = JapiHttpUtil.request(method, url, headerMap, japiClientProperties.getPrefix(), japiClientProperties.getTimeout(), body);
        JapiReturnVo japiReturnVo =
                JapiHttpUtil.request(method, url, headerMap, timeout, body)
                        .apply(japiClientProperties.getPrefix());

        if (log.isDebugEnabled())
            log.debug("{}请求ETC拓展服务平台返回结果\n{}{}", LOG_DIVIDING_LINE, japiReturnVo, LOG_DIVIDING_LINE);

        if (stopWatch.getTotalTimeSeconds() > 5L && log.isInfoEnabled()) log.info("{}", stopWatch.toString());

        int status = japiReturnVo.getStatus();

        // 获取响应结果
        String result = "";

        if (status >= HttpStatus.HTTP_BAD_REQUEST) {
            throw new JapiClientException(japiReturnVo.getStatus(), japiReturnVo.getResult());
        } else if (status == HttpStatus.HTTP_OK && StrUtil.isNotBlank(japiReturnVo.getResult())) {
            result = japiReturnVo.getResult();
        } else if (status != HttpStatus.HTTP_NO_CONTENT || !StrUtil.isBlank(japiReturnVo.getResult())) {
            throw new JapiClientException(japiReturnVo.getStatus());
        }

        // 验证服务端证书序列号
        Assert.isTrue(StrUtil.equals(secure.getSerialNo(CertType.SERVER), japiReturnVo.getSerial()), () -> new JapiClientException("网关平台证书错误"));

        // 拼装响应消息等待签名
        String verifyMessage = secure.buildVerifyMessage(japiReturnVo.getTimestamp(), japiReturnVo.getNonce(), result);
        log.debug("{}响应签名串\n{}{}", LOG_DIVIDING_LINE, verifyMessage, LOG_DIVIDING_LINE);

        // 验证签名
        boolean verify;

        try {
            verify = secure.verify(japiReturnVo.getSignature(), verifyMessage, CertType.SERVER);
        } catch (UnsupportedEncodingException e) {
            throw new JapiClientException("验证签名错误", e);
        }

        Assert.isTrue(verify, () -> new JapiClientException("验证签名失败"));

        return result;
    }

    /**
     * 设置请求头
     *
     * @param timestamp
     * @param nonceStr
     * @param signature
     * @return
     */
    private Map<String, String> header(String timestamp, String nonceStr, String signature) {

        String authorization = japiClientProperties.getAuthorizationType()
                + " channel=" + japiClientProperties.getChannel()
                + ",nonce_str=" + nonceStr
                + ",timestamp=" + timestamp
                + ",signature=" + signature
                + ",serial_no=" + secure.getSerialNo(CertType.CLIENT);

        Map<String, String> headerMap = new HashMap<>(2);
        // 请求添加服务端证书序列号
        headerMap.put("Authorization", authorization);
        headerMap.put(japiClientProperties.getPrefix() + "-Serial", secure.getSerialNo(CertType.SERVER));
        return headerMap;
    }

    /**
     * 将body转换成字符
     *
     * @param body
     * @return
     */
    private String writeValueAsString(Object body) {

        if (Objects.isNull(body)) return "";

        JsonNode jsonNode;
        try {
            jsonNode = MAPPER.convertValue(body, JsonNode.class);
        } catch (Exception e) {
            throw new IllegalArgumentException(String.format("参数\"%s\"序列化失败", body), e);
        }

        return secure.encryptConvert(jsonNode, CertType.SERVER);

    }

    /**
     * 将字符转换成对象
     *
     * @param content
     * @param clazz
     * @param <T>
     * @return
     */
    private <T> T readValue(String content, Class<T> clazz) {
        // 一般在204情况返回空
        if (StrUtil.isBlank(content)) return null;

        String decryptStr = secure.decryptConvert(content);

        try {
            return MAPPER.readValue(decryptStr, clazz);
        } catch (JsonProcessingException e) {
            throw new CallJapiServerException("第三方服务响应结果错误", e);
        }
    }

    /**
     * 将字符转换成对象
     *
     * @param content
     * @param typeReference
     * @param <T>
     * @return
     */
    private <T> T readValue(String content, TypeReference<T> typeReference) {
        // 一般在204情况返回空
        if (StrUtil.isBlank(content)) return null;

        String decryptStr = secure.decryptConvert(content);

        try {
            return MAPPER.readValue(decryptStr, typeReference);
        } catch (JsonProcessingException e) {
            throw new CallJapiServerException("第三方服务响应结果错误", e);
        }
    }

    /**
     * 证书类型
     */
    @Getter
    @AllArgsConstructor
    enum CertType {

        /**
         * client
         */
        CLIENT("client_public_key", "client_serial_no"),

        /**
         * server
         */
        SERVER("server_public_key", "server_serial_no");

        final String publicKey;
        final String serialNo;
    }

    enum ResourceType {

        /**
         * 密文
         */
        ENCRYPT,

        /**
         * 明文
         */
        PLAINTEXT;

        public static ResourceType match(String name) {
            return Stream.of(values()).filter(t -> t.name().equals(name)).findFirst().orElseThrow(() -> new IllegalArgumentException("参数[resource_type]取值必须为(ENCRYPT|PLAINTEXT)"));
        }
    }

    static final class Secure {

        static final String CLIENT_PRIVATE_KEY = "client_private_key";
        // 2022-6-15 去掉静态修饰
        final ConcurrentMap<String, String> CACHE = new ConcurrentHashMap<>();

        Secure(final JapiClientProperties japiClientProperties) {
            init(japiClientProperties);
        }

        /**
         * 初始化
         *
         * @param japiClientProperties
         */
        void init(JapiClientProperties japiClientProperties) {

            loadPrivateKey(japiClientProperties);
            loadPublicKey(japiClientProperties, CertType.CLIENT);
            loadPublicKey(japiClientProperties, CertType.SERVER);
        }

        /**
         * 查找并替换
         *
         * @param jsonNode 原始字符
         * @param key      密钥
         * @param func     应用函数
         * @param cons     替换函数
         * @return 新字符
         */
        JsonNode findResourceApply(JsonNode jsonNode, String key, BiFunction<String, String, String> func, BiConsumer<String, ObjectNode> cons) {

            Objects.requireNonNull(func);
            Objects.requireNonNull(cons);

            // 一级查找resource_type
            if (jsonNode.has(RESOURCE_TYPE)) {
                resourceReplace(jsonNode, key, func, cons);
                return jsonNode;
            }

            // 深度查找resource_type，并获取节点数据
            List<JsonNode> parents = jsonNode.findParents(RESOURCE_TYPE);

            if (CollUtil.isNotEmpty(parents)) {

                for (JsonNode node : parents) {
                    resourceReplace(node, key, func, cons);
                }

                return jsonNode;
            }

            // 一级查找resource
            if (jsonNode.has(RESOURCE)) {
                resourceReplace(jsonNode, key, func, cons);
                return jsonNode;
            }

            // 深度查找resource，并获取节点数据
            parents = jsonNode.findParents(RESOURCE);

            if (CollUtil.isNotEmpty(parents)) {

                for (JsonNode node : parents) {
                    resourceReplace(node, key, func, cons);
                }
            }

            return jsonNode;
        }

        /**
         * resource替换
         *
         * @param jsonNode 原数据
         * @param key      密钥
         * @param func     用应函数
         * @param cons     替换函数
         */
        private void resourceReplace(JsonNode jsonNode, String key, BiFunction<String, String, String> func, BiConsumer<String, ObjectNode> cons) {
            String resource = Optional.ofNullable(jsonNode.get(RESOURCE)).map(JsonNode::toString).orElse("");

            if (StrUtil.isBlank(resource)) {
                log.warn("加解密，{}为空", RESOURCE);
                return;
            }

            String resourceType = Optional.ofNullable(jsonNode.get(RESOURCE_TYPE)).map(JsonNode::textValue).orElse("");

            // 使用处理结果替换原数据
            resourceApply(resourceType, resource, key, func).ifPresent(s -> cons.accept(s, (ObjectNode) jsonNode));
        }

        /**
         * 根据应用函数、原数据类型、证书序列号处理原数据并返回处理后的结果
         *
         * @param resourceType 原数据类型，PLAINTEXT类型不处理，ENCRYPT类型或者为空才处理
         * @param resource     原数据
         * @param key          密钥
         * @param func         应用函数
         * @return 应用结果
         */
        Optional<String> resourceApply(String resourceType, String resource, String key, BiFunction<String, String, String> func) {
            Objects.requireNonNull(func);

            if (StrUtil.isNotBlank(resourceType)) {
                ResourceType type = ResourceType.match(resourceType);

                switch (type) {
                    case ENCRYPT:
                        break;
                    case PLAINTEXT:
                        return Optional.empty();
                }
            }

            return Optional.of(func.apply(resource, key));
        }

        /**
         * 将明文加密并转换成string
         *
         * @param jsonNode
         * @param certType
         * @return
         */
        String encryptConvert(JsonNode jsonNode, CertType certType) {
            return this.findResourceApply(jsonNode, getPublicKey(certType), this::encrypt, (t, u) -> u.replace(RESOURCE, new TextNode(t))).toString();
        }

        /**
         * 将密文解密后转换成JsonNode
         *
         * @param responseBody
         * @return
         */
        String decryptConvert(String responseBody) {

            final BiConsumer<String, ObjectNode> cons = (t, u) -> {
                try {
                    JsonNode newResource = MAPPER.readTree(t);
                    u.replace(RESOURCE, newResource);
                } catch (JsonProcessingException e) {
                    log.error(e.getMessage(), e);
                    throw new IllegalArgumentException("请求参数[resource]解密后格式不符合要求");
                }
            };

            JsonNode jsonNode;

            try {
                jsonNode = MAPPER.readTree(responseBody);
            } catch (JsonProcessingException e) {
                throw new CallJapiServerException("第三方服务响应结果错误", e);
            }

            String privateKey = getPrivateKey();

            return this.findResourceApply(jsonNode, privateKey, this::decrypt, cons).toString();
        }

        /**
         * 获取序列号
         *
         * @param certType
         * @return
         */
        String getSerialNo(CertType certType) {
            return Optional.ofNullable(CACHE.get(certType.getSerialNo()))
                    .orElseThrow(() -> new IllegalArgumentException(certType + "证书不存在，请检查证书配置是否正确"));
        }

        /**
         * 加载公钥
         *
         * @param certType
         * @return
         */
        void loadPublicKey(JapiClientProperties japiClientProperties, CertType certType) {
            if (CACHE.containsKey(certType.publicKey)) return;

            String serverCertPath = CertType.SERVER.equals(certType) ? japiClientProperties.getServerCertPath() : japiClientProperties.getClientCertPath();
            X509Certificate x509Certificate;

            try (InputStream in = FileUtil.getInputStream(serverCertPath)) {
                x509Certificate = (X509Certificate) SecureUtil.readX509Certificate(in);
            } catch (Exception e) {
                throw new IllegalArgumentException("证书[" + serverCertPath + "]解析错误，请检查证书配置是否正确", e);
            }

            String serialNo = x509Certificate.getSerialNumber().toString();
            PublicKey publicKey = x509Certificate.getPublicKey();
            byte[] encoded = publicKey.getEncoded();
            String publicKeyStr = cn.hutool.core.codec.Base64.encode(encoded);

            CACHE.put(certType.publicKey, publicKeyStr);
            CACHE.put(certType.serialNo, serialNo);
        }

        /**
         * 获取公钥
         *
         * @param certType
         * @return
         */
        String getPublicKey(CertType certType) {
            return Optional.ofNullable(CACHE.get(certType.getPublicKey()))
                    .orElseThrow(() -> new IllegalArgumentException(certType + "证书不存在，请检查证书配置是否正确"));
        }

        /**
         * 加载客户端私钥
         *
         * @return
         */
        void loadPrivateKey(JapiClientProperties japiClientProperties) {
            if (CACHE.containsKey(CLIENT_PRIVATE_KEY)) return;

            String path = japiClientProperties.getClientPrivateKeyPath();
            byte[] bytes;

            try {
                bytes = FileUtil.readBytes(path);
            } catch (Exception e) {
                throw new IllegalArgumentException(String.format("私钥解析错误，文件路径\"%s\"", path), e);
            }

            PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(bytes);
            KeyFactory kf;

            try {
                kf = KeyFactory.getInstance(AsymmetricAlgorithm.RSA.getValue());
            } catch (NoSuchAlgorithmException e) {
                throw new IllegalArgumentException("私钥不支持RSA算法");
            }

            byte[] encoded;

            try {
                encoded = kf.generatePrivate(spec).getEncoded();
            } catch (InvalidKeySpecException e) {
                throw new IllegalArgumentException("私钥无效");
            }

            String privateKey = cn.hutool.core.codec.Base64.encode(encoded);

            CACHE.put(CLIENT_PRIVATE_KEY, privateKey);
        }

        /**
         * 获取客户端私钥
         *
         * @return
         */
        String getPrivateKey() {
            return Optional.ofNullable(CACHE.get(CLIENT_PRIVATE_KEY))
                    .orElseThrow(() -> new IllegalArgumentException("客户端私钥不存在，请检查证书配置是否正确"));
        }

        /**
         * 构造验签消息
         *
         * @param timestamp    1970至至今的秒数
         * @param nonceStr     随机串
         * @param responseBody 响应体
         * @return {@link String}
         */
        String buildVerifyMessage(String timestamp, String nonceStr, String responseBody) {

            return timestamp + "\n"
                    + nonceStr + "\n"
                    + responseBody + "\n";
        }

        /**
         * 拼接待验签字符串
         *
         * @param method
         * @param path
         * @param timestamp
         * @param nonceStr
         * @param body
         * @return
         */
        String buildSignatureMessage(String method, String path, String timestamp, String nonceStr, String body) {

            return method + "\n"
                    + path + "\n"
                    + timestamp + "\n"
                    + nonceStr + "\n"
                    + body + "\n";
        }

        /**
         * 验签
         *
         * @param signature
         * @param source
         * @param certType
         * @return
         * @throws UnsupportedEncodingException
         */
        boolean verify(String signature, String source, CertType certType) throws UnsupportedEncodingException {
            return verify(signature, source, getPublicKey(certType));
        }

        /**
         * 验签
         *
         * @param signature
         * @param source
         * @param publicKey
         * @return
         * @throws UnsupportedEncodingException
         */
        boolean verify(String signature, String source, String publicKey) throws UnsupportedEncodingException {
            Sign sign = SecureUtil.sign(SignAlgorithm.SHA256withRSA, null, publicKey);
            return sign.verify(source.getBytes(CHARACTER_ENCODING), cn.hutool.core.codec.Base64.decode(signature));
        }

        /**
         * 签名
         *
         * @param source
         * @return
         */
        String signature(String source) {
            return signature(source, getPrivateKey());
        }

        /**
         * 签名
         *
         * @param source
         * @param privateKey
         * @return
         */
        String signature(String source, String privateKey) {
            Sign sign = SecureUtil.sign(SignAlgorithm.SHA256withRSA, privateKey, null);
            byte[] bytes = sign.sign(StrUtil.bytes(source, CHARACTER_ENCODING));
            return cn.hutool.core.codec.Base64.encode(bytes);
        }

        /**
         * 非对称加密
         *
         * @param source
         * @param publicKey
         * @return
         */
        String encrypt(String source, String publicKey) {
            RSA rsa = SecureUtil.rsa(null, publicKey);
            return rsa.encryptBase64(source, KeyType.PublicKey);
        }

        /**
         * 非对称解密
         *
         * @param source
         * @param privateKey
         * @return
         */
        String decrypt(String source, String privateKey) {
            RSA rsa = SecureUtil.rsa(privateKey, null);
            return rsa.decryptStr(source, KeyType.PrivateKey);
        }

        /**
         * 将明文加密后替换成密文
         *
         * @param jsonNode
         * @param publicKey
         */
        void wrapperEncrypt(JsonNode jsonNode, String publicKey) {
            String resource = jsonNode.get(RESOURCE).toString();
            Assert.notEmpty(resource, "请求加密，{}不能为空", RESOURCE);

            //使用ETC拓展平台证书公钥加密
            String encryptStr = encrypt(resource, publicKey);
            ObjectNode objectNode = (ObjectNode) jsonNode;
            objectNode.replace(RESOURCE, new TextNode(encryptStr));
        }

        /**
         * 将密文解密后替换成功明文
         *
         * @param jsonNode
         * @param privateKey
         * @throws IllegalArgumentException
         */
        void wrapperDecrypt(JsonNode jsonNode, String privateKey) {
            String oldValue = jsonNode.get(RESOURCE).toString();
            Assert.notEmpty(oldValue, "返回结果解密，{}不能为空", RESOURCE);

            String newValue = decrypt(oldValue, privateKey);

            ObjectNode objectNode = (ObjectNode) jsonNode;

            try {
                JsonNode resource = MAPPER.readTree(newValue);
                objectNode.replace(RESOURCE, resource);
            } catch (JsonProcessingException e) {
                log.error(e.getMessage(), e);
                throw new IllegalArgumentException("请求参数[resource]解密后格式不符合要求");
            }
        }

    }
}
